package org.shagin.jfin.data.model;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Currency;
import java.util.Locale;

import javax.swing.event.EventListenerList;

import org.shagin.jfin.core.Debug;
import org.shagin.jfin.data.model.domain.AccountDomain;
import org.shagin.jfin.data.model.domain.CostCenterDomain;
import org.shagin.jfin.data.model.domain.OperationRuleDomain;
import org.shagin.jfin.date.DateUtils;
import org.shagin.jfin.date.cond.DateRange;
import org.shagin.jfin.model.event.AccountEvent;
import org.shagin.jfin.model.event.BudgetEvent;
import org.shagin.jfin.model.event.BudgetListener;
import org.shagin.jfin.model.event.CostCenterEvent;
import org.shagin.jfin.model.event.OperationCategoryEvent;
import org.shagin.jfin.model.event.OperationRuleEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Class Budget.
 */
public class Budget extends IdObject implements IBudget {

    private static final Logger log = LoggerFactory.getLogger(Budget.class);

    private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
            this);
    private DateRange range = getDefaultDateRange();
    private CostCenterDomain costCenterDomain = new CostCenterDomain(this);
    private AccountDomain accountDomain = new AccountDomain(this);
    private OperationRuleDomain operationRuleDomain = new OperationRuleDomain(
            this);
    private EventListenerList listenerList = new EventListenerList();

    /**
     * Instantiates a new budget.
     * 
     * @param id the id
     */
    public Budget(String id) {
        super(id);
    }

    /**
     * Gets the default date range.
     * 
     * @return the default date range
     */
    public static DateRange getDefaultDateRange() {
        return new DateRange(DateUtils.now(), DateUtils.incMonth(
                DateUtils.now(), 6));
    }

    /**
     * Gets the account domain.
     * 
     * @return the account domain
     */
    @Override
    public AccountDomain getAccountDomain() {
        return accountDomain;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public OperationRuleDomain getOperationRuleDomain() {
        return operationRuleDomain;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public CostCenterDomain getCostCenterDomain() {
        return costCenterDomain;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Currency getDefaultCurrency() {
        return Currency.getInstance(Locale.getDefault());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double totalAmount() {
        return accountDomain.getAmount(getDefaultCurrency()).getValue();
    }

    /**
     * Fire changed event.
     * 
     * @param e the e
     */
    private void fireChangedEvent(BudgetEvent e) {
        Debug.traceEnter(log);
        for (BudgetListener l : listenerList.getListeners(BudgetListener.class)) {
            l.changed(e);
        }
    }

    /**
     * Fire account changed event.
     * 
     * @param e the e
     */
    private void fireAccountChangedEvent(AccountEvent e) {
        for (BudgetListener l : listenerList.getListeners(BudgetListener.class)) {
            l.accountChanged(e);
        }
    }

    /**
     * Fire account added event.
     * 
     * @param e the e
     */
    private void fireAccountAddedEvent(AccountEvent e) {
        for (BudgetListener l : listenerList.getListeners(BudgetListener.class)) {
            l.accountAdded(e);
        }
    }

    /**
     * Fire changed event.
     */
    @Override
    public void fireChangedEvent() {
        Debug.traceEnter(log);
        fireChangedEvent(new BudgetEvent(this));
    }

    /**
     * Fire account added event.
     * 
     * @param account the account
     */
    @Override
    public void fireAccountAddedEvent(Account account) {
        fireAccountAddedEvent(new AccountEvent(this, account));
        fireChangedEvent();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void fireAccountChangedEvent(Account account) {
        fireAccountChangedEvent(new AccountEvent(this, account));
        fireChangedEvent();
    }

    /**
     * Adds the budget listener.
     * 
     * @param l the l
     */
    @Override
    public void addBudgetListener(BudgetListener l) {
        listenerList.add(BudgetListener.class, l);
    }

    /**
     * Removes the budget listener.
     * 
     * @param l the l
     */
    @Override
    public void removeBudgetListener(BudgetListener l) {
        listenerList.remove(BudgetListener.class, l);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void fireCostCenterChangedEvent(CostCenter costCenter) {
        fireCostCenterChangedEvent(new CostCenterEvent(this, costCenter));
        fireChangedEvent();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void fireCostCenterAddedEvent(CostCenter costCenter) {
        fireCostCenterAddedEvent(new CostCenterEvent(this, costCenter));
        fireChangedEvent();
    }

    /**
     * Fire cost center changed event.
     * 
     * @param e the e
     */
    private void fireCostCenterChangedEvent(CostCenterEvent e) {
        for (BudgetListener l : listenerList.getListeners(BudgetListener.class)) {
            l.costCenterChanged(e);
        }
    }

    /**
     * Fire cost center added event.
     * 
     * @param e the e
     */
    private void fireCostCenterAddedEvent(CostCenterEvent e) {
        for (BudgetListener l : listenerList.getListeners(BudgetListener.class)) {
            l.costCenterAdded(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void fireOperationRuleChangedEvent(OperationRule operationRule) {
        Debug.traceEnter(log);
        fireOperationRuleChangedEvent(new OperationRuleEvent(this,
                operationRule));
        fireChangedEvent();
    }

    /**
     * Fire operation rule changed event.
     * 
     * @param e the e
     */
    private void fireOperationRuleChangedEvent(OperationRuleEvent e) {
        Debug.traceEnter(log);
        for (BudgetListener l : listenerList.getListeners(BudgetListener.class)) {
            l.operationRuleChanged(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DateRange getDateRange() {
        return range;
    }

    /**
     * Fire operation rule added event.
     * 
     * @param e the e
     */
    private void fireOperationRuleAddedEvent(OperationRuleEvent e) {
        Debug.traceEnter(log);
        for (BudgetListener l : listenerList.getListeners(BudgetListener.class)) {
            l.operationRuleAdded(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void fireOperationRuleAddedEvent(OperationRule operationRule) {
        fireOperationRuleAddedEvent(new OperationRuleEvent(this, operationRule));
        fireChangedEvent();
    }

    /**
     * Fire operation rule deleted event.
     * 
     * @param e the e
     */
    private void fireOperationRuleDeletedEvent(OperationRuleEvent e) {
        Debug.traceEnter(log);
        for (BudgetListener l : listenerList.getListeners(BudgetListener.class)) {
            l.operationRuleDeleted(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void fireOperationRuleDeletedEvent(OperationRule operationRule) {
        fireOperationRuleDeletedEvent(new OperationRuleEvent(this,
                operationRule));
        fireChangedEvent();
    }

    /**
     * Fire operation category added event.
     * 
     * @param e the e
     */
    private void fireOperationCategoryAddedEvent(OperationCategoryEvent e) {
        Debug.traceEnter(log);
        for (BudgetListener l : listenerList.getListeners(BudgetListener.class)) {
            l.operationCategoryAdded(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void fireOperationCategoryAddedEvent(
            OperationCategory operationCategory) {
        fireOperationCategoryAddedEvent(new OperationCategoryEvent(this,
                operationCategory));
        fireChangedEvent();
    }

    /**
     * Fire operation category deleted event.
     * 
     * @param e the e
     */
    private void fireOperationCategoryDeletedEvent(OperationCategoryEvent e) {
        Debug.traceEnter(log);
        for (BudgetListener l : listenerList.getListeners(BudgetListener.class)) {
            l.operationCategoryDeleted(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void fireOperationCategoryDeletedEvent(
            OperationCategory operationCategory) {
        fireOperationCategoryDeletedEvent(new OperationCategoryEvent(this,
                operationCategory));
        fireChangedEvent();

    }

    /**
     * Fire operation category changed event.
     * 
     * @param e the e
     */
    private void fireOperationCategoryChangedEvent(OperationCategoryEvent e) {
        Debug.traceEnter(log);
        for (BudgetListener l : listenerList.getListeners(BudgetListener.class)) {
            l.operationCategoryChanged(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void fireOperationCategoryChangedEvent(
            OperationCategory operationCategory) {
        fireOperationCategoryChangedEvent(new OperationCategoryEvent(this,
                operationCategory));
        fireChangedEvent();
    }

    /**
     * Adds the property change listener.
     * 
     * @param listener the listener
     */
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.addPropertyChangeListener(listener);
    }

    /**
     * Removes the property change listener.
     * 
     * @param listener the listener
     */
    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.removePropertyChangeListener(listener);
    }

    /**
     * Fire property change.
     *
     * @param propertyName the property name
     * @param oldValue the old value
     * @param newValue the new value
     */
    public void firePropertyChange(String propertyName, Object oldValue,
            Object newValue) {
        propertyChangeSupport.firePropertyChange(propertyName, oldValue,
                newValue);
    }

    /**
     * Fire property change.
     *
     * @param event the event
     */
    public void firePropertyChange(PropertyChangeEvent event) {
        propertyChangeSupport.firePropertyChange(event);
    }
}
